package com.gitee.loyo.interfaces

import com.gitee.loyo.KSQL
import com.gitee.loyo.Utils
import com.gitee.loyo.jdbc.JDBC
import com.gitee.loyo.jdbc.TypeMapping
import java.lang.RuntimeException
import java.sql.ResultSet
import java.sql.ResultSetMetaData
import kotlin.reflect.KClass
import kotlin.reflect.full.isSubclassOf

fun interface ResultSetHandler {

    companion object{
        val DEFAULT = object : ResultSetHandler{

            override fun handle(rs: ResultSet, typeRef: TypeReference<*>): List<Any> {
                val list = ArrayList<Any>()
                when {
                    typeRef.isParameterizedPair() -> {
                        val (type1, type2) = typeRef.getPairTypes()
                        populateKObject(rs, list, type1, type2)
                    }
                    typeRef.isParameterizedTriple() -> {
                        val (type1, type2, type3) = typeRef.getTripleTypes()
                        populateKObject(rs, list, type1, type2, type3)
                    }
                    else -> {
                        populateKObject(rs, list, typeRef.kotlinType())
                    }
                }

                when(list.size){
                    0 -> KSQL.log("==> []")
                    1 -> KSQL.log("==> {}", list[0])
                    else -> KSQL.log("==> [{}]", list.size)
                }
                return list
            }

            private fun populateKObject(rs: ResultSet, list: ArrayList<Any>, vararg types: KClass<*>){
                val metadata = rs.metaData
                val fieldNames = ArrayList<List<String>>()
                while (rs.next()){
                    when(types.size){
                        1 -> {
                            createKObject(rs, metadata, fieldNames, types[0])?.also { list.add(it) }
                        }
                        2 -> {
                            list.add(createKObject(rs, metadata, fieldNames, types[0], 1) to createKObject(rs, metadata, fieldNames, types[1], 2))
                        }
                        3 -> {
                            list.add(Triple(
                                createKObject(rs, metadata, fieldNames, types[0], 1),
                                createKObject(rs, metadata, fieldNames, types[1], 2),
                                createKObject(rs, metadata, fieldNames, types[2], 3)
                            ))
                        }
                    }
                }
            }

            private fun createKObject(rs: ResultSet, metadata: ResultSetMetaData, fieldNames: ArrayList<List<String>>, type: KClass<*>, pos: Int = 1): Any?{
                when{
                    Utils.isNotObject(type) -> {
                        if(pos > metadata.columnCount){
                            throw RuntimeException("返回数据为" + metadata.columnCount + "列")
                        }
                        return if(!rs.wasNull()){
                            val resultSetHandler = JDBC.resultSetHandlerMap[type]
                                ?: throw RuntimeException("未找到" + type.qualifiedName + ", 请使用JDBC.putResultSetHandler添加相应类型处理")
                            resultSetHandler.handle(rs, pos)
                        }else{
                            null
                        }
                    }
                    type.isData -> {
                        val obj = HashMap<String, Any?>()
                        populateValues(rs, metadata, fieldNames, type){ memberNames, value ->
                            obj[memberNames.joinToString(".")] = value
                        }
                        return KSQL.reflection.newDataInstance(type, obj)
                    }
                    type == Map::class -> {
                        val obj = LinkedHashMap<String, Any?>()
                        populateValues(rs, metadata, fieldNames, type){ memberNames, value ->
                            obj[memberNames.joinToString(".")] = value
                        }
                        return obj
                    }
                    else -> {
                        val obj = KSQL.reflection.newInstance(type)
                        populateValues(rs, metadata, fieldNames, type){ memberNames, value ->
                            KSQL.reflection.setFieldValue(obj, value, memberNames)
                        }
                        return obj
                    }
                }
            }

            private fun populateValues(rs: ResultSet, metadata: ResultSetMetaData, fieldNames: ArrayList<List<String>>, type: KClass<*>, fn: (List<String>, Any) -> Unit){
                for (i in 0 until metadata.columnCount){
                    if(i >= fieldNames.size){
                        val columnName = metadata.getColumnName(i + 1)
                        val columnLabel = metadata.getColumnLabel(i + 1)
                        val fieldName = if(columnLabel == columnName){
                            KSQL.nameMappingStrategy.toFieldName(columnLabel)
                        }else{
                            columnLabel
                        }
                        fieldNames.add(fieldName.split("."))
                    }

                    val memberNames = fieldNames[i]
                    val fieldType = if(type.isSubclassOf(Map::class)){
                        TypeMapping.toJavaType(metadata.getColumnType(i + 1)).kotlin
                    }else{
                        KSQL.reflection.getFieldType(type, memberNames) ?: continue
                    }
                    val resultSetHandler = JDBC.resultSetHandlerMap[fieldType]
                        ?: throw RuntimeException("未找到${fieldType.qualifiedName}, 请使用JDBC.putResultSetHandler添加相应类型处理")
                    val value = resultSetHandler.handle(rs, i + 1)
                    if(value != null){
                        fn(memberNames, value)
                    }
                }
            }
        }
    }

    fun handle(rs: ResultSet, typeRef: TypeReference<*>): List<Any>
}